package org.fireflow2.designer.eclipse.editors;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.List;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocumentPartitioner;
import org.eclipse.jface.text.rules.FastPartitioner;
import org.eclipse.jface.text.source.CompositeRuler;
import org.eclipse.jface.text.source.SourceViewer;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorReference;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.ISelectionListener;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.actions.WorkspaceModifyOperation;
import org.eclipse.ui.ide.IDE;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.part.MultiPageEditorPart;
import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
import org.eclipse.ui.views.properties.IPropertySheetPage;
import org.eclipse.ui.views.properties.tabbed.ITabbedPropertySheetPageContributor;
import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetPage;
import org.fireflow.model.InvalidModelException;
import org.fireflow.model.io.SerializerException;
import org.fireflow.pdl.fpdl20.io.FPDLDeserializer;
import org.fireflow.pdl.fpdl20.io.FPDLSerializer;
import org.fireflow.pdl.fpdl20.process.WorkflowProcess;
import org.fireflow.pdl.fpdl20.process.impl.WorkflowProcessImpl;
import org.fireflow2.designer.eclipse.Activator;
import org.fireflow2.designer.eclipse.editors.xml.ColorManager;
import org.fireflow2.designer.eclipse.editors.xml.XMLConfiguration;
import org.fireflow2.designer.eclipse.editors.xml.XMLPartitionScanner;
import org.fireflow2.designer.eclipse.modelwrapper.SubProcessWrapper;
import org.fireflow2.designer.eclipse.modelwrapper.WorkflowProcessWrapper;
import org.fireflow2.designer.eclipse.outline.WorkflowProcessContentOutlinePage;
import org.fireflow2.designer.eclipse.simulation.SimulatorPanel;
import org.fireflow2.designer.eclipse.util.Util4Eclipse;

/**
 * An example showing how to create a multi-page processDesignPanel. This
 * example has 3 pages:
 * <ul>
 * <li>page 0 contains a nested text processDesignPanel.
 * <li>page 1 allows you to change the font used in page 2
 * <li>page 2 shows the words in page 0 in sorted order
 * </ul>
 */
public class FireflowMultiPageEditor extends MultiPageEditorPart implements
		IResourceChangeListener, ISelectionListener,
		ITabbedPropertySheetPageContributor, PropertyChangeListener {
	WorkflowProcess workflowProcess = null;
	WorkflowProcessWrapper workflowProcessWrapper = null;

	/** The text processDesignPanel used in page 0. */
	private WorkflowProcessDesignerPanel processDesignPanel;
	private SimulatorPanel simulationPanel;
	private Document workflowXmlSourceDoc = null;
	private IContentOutlinePage contentOutlinePage = null;

	private SelectionSynchronizer selectionSynchronizer = null;

	private String openFileErrorMessage = null;


	/**
	 * Creates a multi-page processDesignPanel example.
	 */
	public FireflowMultiPageEditor() {
		super();
		selectionSynchronizer = new SelectionSynchronizer();
	}

	/**
	 * Creates page 0 of the multi-page processDesignPanel, which contains a
	 * text processDesignPanel.
	 */
	void createPage0() {

		try {
			// processDesignPanel = new TextEditor();
			int index = this.addPage(processDesignPanel, this.getEditorInput());

			setPageText(index, "Designer");// processDesignPanel.getTitle());
		} catch (PartInitException e) {
			System.out.println(e.getMessage());
			e.printStackTrace();
			ErrorDialog.openError(getSite().getShell(),
					"Error creating nested text processDesignPanel", null, e
							.getStatus());
		}
	}

	/**
	 * Creates page 1 of the multi-page processDesignPanel, which allows you to
	 * change the font used in page 2.
	 */
	void createPage1() {
		try {
			// processDesignPanel = new TextEditor();

			int index = addPage(simulationPanel, getEditorInput());
			// int index = addPage(new TextEditor(),getEditorInput());
			
			setPageText(index, "Simulator");// processDesignPanel.getTitle());
		} catch (PartInitException e) {
			System.out.println(e.getMessage());
			e.printStackTrace();
			ErrorDialog.openError(getSite().getShell(),
					"Error creating nested text processDesignPanel", null, e
							.getStatus());
		}

	}

	/**
	 * Creates page 2 of the multi-page processDesignPanel, which shows the
	 * sorted text.
	 */
	void createPage2() {
		Composite composite = new Composite(getContainer(), SWT.NONE);
		FillLayout layout = new FillLayout();
		composite.setLayout(layout);
		CompositeRuler ruler = new CompositeRuler();// 创建一个标尺ruler

		// LineNumberRulerColumn lineNumberRuler = new LineNumberRulerColumn();
		// ruler.addDecorator(0, lineNumberRuler);//为这个标尺添加行号显示

		SourceViewer sourceViewer = new SourceViewer(composite, ruler,
				SWT.V_SCROLL | SWT.FULL_SELECTION | SWT.H_SCROLL);

		workflowXmlSourceDoc = new Document("");

		IDocumentPartitioner partitioner = new FastPartitioner(
				new XMLPartitionScanner(), new String[] {
						XMLPartitionScanner.XML_TAG,
						XMLPartitionScanner.XML_COMMENT });
		partitioner.connect(workflowXmlSourceDoc);
		workflowXmlSourceDoc.setDocumentPartitioner(partitioner);
		
		sourceViewer.setDocument(workflowXmlSourceDoc);
		ColorManager colorManager = new ColorManager();
		sourceViewer.configure(new XMLConfiguration(colorManager));
		sourceViewer.setEditable(false);

		// 设置字体
		sourceViewer.getTextWidget().setFont(
				new Font(sourceViewer.getControl().getDisplay(), new FontData(
						"Courier", 8, SWT.NORMAL)));
		sourceViewer.getTextWidget().setLineSpacing(5);

		int index = this.addPage(composite);
		setPageText(index, "Source");// processDesignPanel.getTitle());
	}

	/**
	 * Creates the pages of the multi-page processDesignPanel.
	 */
	protected void createPages() {
		createPage0();
		createPage1();
		createPage2();
		if (this.openFileErrorMessage!=null){
			this.setActivePage(2);
		}		
		String name = this.workflowProcessWrapper.getName();
		String dispName = this.workflowProcessWrapper.getDisplayName();
		this.setPartName(((dispName==null || dispName.trim().equals(""))?name:dispName));		
	}

	/**
	 * The <code>MultiPageEditorPart</code> implementation of this
	 * <code>IWorkbenchPart</code> method disposes all nested editors.
	 * Subclasses may extend.
	 */
	public void dispose() {
		ResourcesPlugin.getWorkspace().removeResourceChangeListener(this);
		super.dispose();
	}

	/**
	 * Saves the multi-page processDesignPanel's document.
	 */
	public void doSave(IProgressMonitor monitor) {
		// this.getEditorInput().get
		IFile file = ((IFileEditorInput) this.getEditorInput()).getFile();
		if (file.isReadOnly()){
			MessageDialog.openError(null, "Error",
			"The file is read only.");
			return ;
		}
		// JAXP_FPDL_Serializer ser = new JAXP_FPDL_Serializer();
		FPDLSerializer ser = new FPDLSerializer();
		java.io.ByteArrayOutputStream byteArrOut = new java.io.ByteArrayOutputStream();
		try {
			// ser.setEncoding("UTF-8");
			ser.serialize((WorkflowProcess) this.workflowProcessWrapper
					.getWorkflowModelElement(), byteArrOut);

			java.io.ByteArrayInputStream byteArrIn = new java.io.ByteArrayInputStream(
					byteArrOut.toByteArray());

			file.setContents(byteArrIn, IFile.FORCE, monitor);
			workflowProcessWrapper.setDirty(false);
			this.firePropertyChange(PROP_DIRTY);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SerializerException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (CoreException e) {
			e.printStackTrace();
		} catch (InvalidModelException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	/**
	 * Saves the multi-page processDesignPanel's document as another file. Also
	 * updates the text for page 0's tab, and updates this multi-page
	 * processDesignPanel's input to correspond to the nested
	 * processDesignPanel's.
	 */
	public void doSaveAs() {
		Shell shell = getSite().getWorkbenchWindow().getShell();
		FileDialog fd = new FileDialog(shell, SWT.SAVE);
		String selectedfile = null;
		try {
			// set the standard file name
			fd.setFileName(getEditorInput().getName());

			// set the filter extensions
			fd.setFilterExtensions(new String[] { "*.xml" });
			selectedfile = fd.open();
		} catch (Exception ex) {
			ex.printStackTrace();
		}

		// return, if the dialog was aborted
		if (selectedfile == null) {
			return;
		}

		// the file
		final IPath path = new Path(selectedfile);
		final File file = path != null ? path.toFile() : null;

		// return, if file could not be created
		if (file == null) {
			MessageDialog.openError(getEditorSite().getShell(), "Error",
					"Save as failed, can not find the file");

			return;
		}

		// return, if destination file is currently open

		IWorkbenchPage workbenchPage = Activator.getDefault().getWorkbench()
				.getActiveWorkbenchWindow().getActivePage();

		for (IEditorReference editorReference : workbenchPage
				.getEditorReferences()) {
			String openFileName = "";
			try {
				openFileName = editorReference.getEditorInput().getName();
			} catch (PartInitException e) {
			}

			if (openFileName.equals(selectedfile)) {
				MessageDialog.openError(getEditorSite().getShell(), "Error",
						"文件已经被打开，不能覆盖");
				return;
			}
		}

		// return, if the file already exists and should not be overwritten
		if (file.exists()
				&& !MessageDialog.openQuestion(getEditorSite().getShell(),
						"Warn", "文件" + selectedfile + "已经存在，是否覆盖？")) {
			return;
		}

		// save
		try {
			WorkspaceModifyOperation operation = new WorkspaceModifyOperation() {

				@Override
				public void execute(final IProgressMonitor monitor) {
					try {
						java.io.FileOutputStream fout = new java.io.FileOutputStream(
								file);
//						Dom4JFPDLSerializer ser = new Dom4JFPDLSerializer();
						FPDLSerializer ser = new FPDLSerializer();
						// ser.setEncoding("UTF-8");
						ser.serialize((WorkflowProcess) workflowProcessWrapper
								.getWorkflowModelElement(), fout);

						// setInputWithNotify(new
						// FileEditorInput(ResourcesPlugin.getWorkspace().getRoot().getFile(path)));
						workflowProcessWrapper.setDirty(false);
						firePropertyChange(PROP_DIRTY);
						monitor.done();
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					} catch (SerializerException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					} catch (InvalidModelException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			};

			new ProgressMonitorDialog(shell).run(false, // don't fork
					false, // not cancelable
					operation);
		} catch (InterruptedException ie) {
			// should not happen, since the monitor dialog is not
			// cancelable
			ie.printStackTrace();
		} catch (InvocationTargetException ite) {
			ite.printStackTrace();
		} catch (Exception e) {
			MessageDialog.openError(getEditorSite().getShell(), "Error", e
					.getMessage());
		}
	}

	/*
	 * (non-Javadoc) Method declared on IEditorPart
	 */
	public void gotoMarker(IMarker marker) {
		setActivePage(0);
		IDE.gotoMarker(getEditor(0), marker);
	}

	/**
	 * The <code>MultiPageEditorExample</code> implementation of this method
	 * checks that the input is an instance of <code>IFileEditorInput</code>.
	 */
	public void init(IEditorSite site, IEditorInput editorInput)
			throws PartInitException {
		if (!(editorInput instanceof IFileEditorInput))
			throw new PartInitException(
					"Invalid Input: Must be IFileEditorInput");
		super.init(site, editorInput);

		List<SubProcessWrapper> subflowWrappers = workflowProcessWrapper.getSubProcessWrappers();
		processDesignPanel = new WorkflowProcessDesignerPanel(
				subflowWrappers.get(0), this);
		simulationPanel = new SimulatorPanel(subflowWrappers.get(0), this);

		if (contentOutlinePage == null) {
			contentOutlinePage = new WorkflowProcessContentOutlinePage(
					this.processDesignPanel, this.simulationPanel,
					subflowWrappers.get(0));
		}


		this.getSite().setSelectionProvider(this.selectionSynchronizer);

		ResourcesPlugin.getWorkspace().addResourceChangeListener(this);
		getSite().getWorkbenchWindow().getSelectionService()
				.addSelectionListener(this);

		workflowProcessWrapper.addPropertyChangeListener(this);

	}

	/*
	 * (non-Javadoc) Method declared on IEditorPart.
	 */
	public boolean isSaveAsAllowed() {
		return true;
	}

	public boolean isDirty() {
		if (workflowProcessWrapper != null) {
			return this.workflowProcessWrapper.isDirty();
		} else {
			return false;
		}
	}

	/**
	 * Calculates the contents of page 2 when the it is activated.
	 */
	protected void pageChange(int newPageIndex) {
		super.pageChange(newPageIndex);
		if (newPageIndex == 0) {
		} else if (newPageIndex == 1) {
			//TODO 2.0暂时注释，待恢复
//			FireflowSimulator.getCurrentSimulator().setCurrentSimulatorPanel(
//					simulationPanel);
		} else if (newPageIndex == 2) {
			if (this.openFileErrorMessage == null) {
				FPDLSerializer ser = new FPDLSerializer();
				java.io.ByteArrayOutputStream byteArrOut = new java.io.ByteArrayOutputStream();
				try {
					ser.serialize((WorkflowProcess) this.workflowProcessWrapper
							.getWorkflowModelElement(), byteArrOut);
					this.workflowXmlSourceDoc.set(byteArrOut.toString("utf-8"));
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (SerializerException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (InvalidModelException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			} else {
				this.workflowXmlSourceDoc.set(this.openFileErrorMessage);
			}
		}
	}

	/**
	 * 当Project explorer中的项目关闭时，该方法用于关闭并清理Editor
	 */
	public void resourceChanged(final IResourceChangeEvent event) {

		if (event.getType() == IResourceChangeEvent.PRE_CLOSE) {
			Display.getDefault().asyncExec(new Runnable() {
				public void run() {
					IWorkbenchPage[] pages = getSite().getWorkbenchWindow()
							.getPages();
					for (int i = 0; i < pages.length; i++) {
						if (((FileEditorInput) processDesignPanel
								.getEditorInput()).getFile().getProject()
								.equals(event.getResource())) {
							IEditorPart editorPart = pages[i]
									.findEditor(processDesignPanel
											.getEditorInput());
							pages[i].closeEditor(editorPart, true);
						}
					}
				}
			});
		}

	}

	protected void setInput(IEditorInput input) {
		super.setInput(input);
		
		IFile file = ((IFileEditorInput) input).getFile();

		try { // attempt to read from a file
			InputStream istream = file.getContents(false);

			 FPDLDeserializer parser = new FPDLDeserializer();

			this.workflowProcess = parser.deserialize(istream);
			workflowProcessWrapper = new WorkflowProcessWrapper(workflowProcess);
		
			workflowProcessWrapper.setProject(file.getProject());
		} catch (Exception e) { // but if there's an error, create a new diagram
			this.openFileErrorMessage = Util4Eclipse.errorStackToString(e);
			this.workflowProcess = new WorkflowProcessImpl("ERROR","ERROR");
			workflowProcessWrapper = new WorkflowProcessWrapper(workflowProcess);
		}
	}

	

	@Override
	public Object getAdapter(Class type) {
		if (type == IContentOutlinePage.class){
			return this.getContentOutlinePage();

		}else if (type == IPropertySheetPage.class) {
			return new TabbedPropertySheetPage(this);
		}

		if (processDesignPanel != null) {
			return processDesignPanel.getAdapter(type);
		} else {
			return super.getAdapter(type);
		}
	}

	private IContentOutlinePage getContentOutlinePage() {
		return contentOutlinePage;
	}

	public void selectionChanged(IWorkbenchPart part, ISelection selection) {
		if (this.equals(getSite().getPage().getActiveEditor())) {
			processDesignPanel.updateSelectionActions();
		}
	}

	public String getContributorId() {
		return "org.fireflow2.designer.eclipse.properties.FireflowPropertyContributor";
	}

	public SelectionSynchronizer getSelectionSynchronizer() {
		return this.selectionSynchronizer;
	}

	public void propertyChange(PropertyChangeEvent evt) {
		if (!evt.getPropertyName().startsWith("SIMULATION_PROPERTY")){
			this.firePropertyChange(PROP_DIRTY);
		}
	}
}
